﻿// Copyright (c) 2014 Morten Bakkedal
// This code is published under the MIT License.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

using FuncLib.DualFunctions.Collections;

namespace FuncLib.DualFunctions.Compilation
{
	internal class SortedGradient : HashedList<DualVariable>
	{
		private Dictionary<int, Dictionary<SortedGradient, IndexList>> indexLists = new Dictionary<int, Dictionary<SortedGradient, IndexList>>();
		private bool hasCount;
		private int hessianCount;

		public SortedGradient()
		{
			hasCount = false;
		}

		public IndexList GetIndexList(int startIndex, SortedGradient gradient, Storage gradientStorage) // GetIndexList indexLists
		{
			int relativeIndex = gradientStorage.Index - startIndex;

			if (!indexLists.ContainsKey(relativeIndex))
			{
				indexLists.Add(relativeIndex, new Dictionary<SortedGradient, IndexList>());
			}

			IndexList computed;
			if (!indexLists[relativeIndex].TryGetValue(gradient, out computed))
			{
				List<int> offsets = new List<int>(); // no need to use a dynamic array
				foreach (DualVariable variable in this)
				{
					offsets.Add(gradient.IndexOf(variable) + relativeIndex); // FIXME use SortedDictionary for the lookup
				}
				computed = new IndexList(offsets.ToArray());

				indexLists[relativeIndex].Add(gradient, computed);
			}

			return computed;
		}

		public static IList<DualVariable> Merge(IList<DualVariable> variables1, IList<DualVariable> variables2)
		{
			List<DualVariable> variables = new List<DualVariable>();

			foreach (DualVariable variable in variables1)
			{
				variables.Add(variable);
			}

			foreach (DualVariable variable in variables2)
			{
				if (!variables.Contains(variable))
				{
					variables.Add(variable);
				}
			}

			variables.Sort(delegate(DualVariable v1, DualVariable v2) { return v1.FunctionIndex.CompareTo(v2.FunctionIndex); });

			return variables;
		}

		private void UpdateCount()
		{
			hessianCount = 0;

			IList<DualVariable> variables = this;

			for (int i = 0; i < variables.Count; i++)
			{
				DualVariable variable1 = variables[i];

				if (!DualVariable.CheckFirstOrder(variable1))
				{
					continue;
				}

				for (int j = i; j < variables.Count; j++)
				{
					DualVariable variable2 = variables[j];

					if (!DualVariable.CheckSecondOrder(variable1, variable2))
					{
						continue;
					}

					hessianCount++;
				}
			}

			hasCount = true;
		}

		public int HessianCount
		{
			get
			{
				if (!hasCount)
				{
					UpdateCount();
				}

				return hessianCount;
			}
		}
	}
}
